# json_client.py
import requests
from typing import Optional, Dict, Any, Union


class JsonHttpClient:
    """
    一个面向对象的 requests 封装，专为JSON API设计。

    特性:
    - 设置默认的 base_url。
    - 默认使用 JSON 进行请求和响应处理。
    - 统一的错误处理和日志记录。
    - 使用 requests.Session 提高性能。
    """

    def __init__(self, base_url: str="http://127.0.0.1:8080", default_headers: Optional[Dict[str, str]] = None, timeout: int = 10):
        """
        初始化JSON HTTP客户端。

        :param base_url: API的基础域名，例如 "https://api.example.com/v1"。
        :param default_headers: 所有请求都会携带的默认请求头。
        :param timeout: 每个请求的默认超时时间（秒）。
        """
        # 确保base_url不以/结尾，方便后续拼接
        self.base_url = base_url.rstrip('/')
        self.timeout = timeout

        # 使用Session对象来管理连接池和保持Cookie
        self.session = requests.Session()

        # 设置默认请求头
        headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
        if default_headers:
            headers.update(default_headers)
        self.session.headers.update(headers)

    def _request(self, method: str, endpoint: str, **kwargs) -> Optional[Dict[Any, Any]]:
        """
        内部统一的请求方法。

        :param method: HTTP方法 (GET, POST, PUT, DELETE等)。
        :param endpoint: API端点，例如 "/users/123"。
        :param kwargs: 传递给requests.request的其他参数。
        :return: 成功时返回解析后的JSON字典，失败时返回None。
        """
        # 拼接完整的URL
        url = f"{self.base_url}/{endpoint.lstrip('/')}"

        # 设置默认超时，允许在调用时覆盖
        kwargs.setdefault('timeout', self.timeout)

        try:
            # 发送请求
            response = self.session.request(method, url, **kwargs)
            # 如果HTTP状态码表示错误 (4xx or 5xx)，则抛出异常
            response.raise_for_status()

            # 尝试将响应解析为JSON
            return response.json()

        except requests.exceptions.HTTPError as e:
            print(f"❌ HTTP错误: {e.response.status_code} - {e.response.text}")
        except requests.exceptions.ConnectionError as e:
            print(f"❌ 连接错误: 无法连接到服务器 {url}")
        except requests.exceptions.Timeout as e:
            print(f"❌ 请求超时: {url}")
        except requests.exceptions.JSONDecodeError:
            print(f"❌ JSON解析失败: 服务器返回了非JSON格式的响应。")
        except requests.exceptions.RequestException as e:
            print(f"❌ 请求发生未知异常: {e}")

        return None

    def get(self, endpoint: str, params: Optional[Dict[str, Any]] = None, **kwargs) -> Optional[Dict[Any, Any]]:
        """
        发送GET请求。

        :param endpoint: API端点。
        :param params: URL查询参数。
        :return: 解析后的JSON字典或None。
        """
        return self._request("GET", endpoint, params=params, **kwargs)

    def post(self, endpoint: str, data: Optional[Dict[str, Any]] = None, **kwargs) -> Optional[Dict[Any, Any]]:
        """
        发送POST请求，默认将data作为JSON体发送。

        :param endpoint: API端点。
        :param data: 要发送的JSON数据（字典）。
        :return: 解析后的JSON字典或None。
        """
        # 如果调用者传了json参数，优先使用它，否则将data作为json参数
        if 'json' not in kwargs and data is not None:
            kwargs['json'] = data
        return self._request("POST", endpoint, **kwargs)

    def put(self, endpoint: str, data: Optional[Dict[str, Any]] = None, **kwargs) -> Optional[Dict[Any, Any]]:
        """
        发送PUT请求，默认将data作为JSON体发送。

        :param endpoint: API端点。
        :param data: 要发送的JSON数据（字典）。
        :return: 解析后的JSON字典或None。
        """
        if 'json' not in kwargs and data is not None:
            kwargs['json'] = data
        return self._request("PUT", endpoint, **kwargs)

    def delete(self, endpoint: str, **kwargs) -> Optional[Dict[Any, Any]]:
        """
        发送DELETE请求。

        :param endpoint: API端点。
        :return: 解析后的JSON字典或None。
        """
        return self._request("DELETE", endpoint, **kwargs)


# --- 使用示例 ---
if __name__ == "__main__":
    # 1. 初始化客户端，指向一个公共的测试API
    # JSONPlaceholder 是一个免费的在线REST API，常用于测试
    client = JsonHttpClient(base_url="https://jsonplaceholder.typicode.com")

    # 2. 使用GET请求获取一个帖子
    print("--- 1. 获取一个帖子 (GET) ---")
    post = client.get("/posts/1")
    if post:
        print(f"帖子标题: {post['title']}")
        print(f"帖子内容: {post['body'][:50]}...")

    # 3. 使用POST请求创建一个新帖子
    print("\n--- 2. 创建一个新帖子 (POST) ---")
    new_post_data = {
        'title': '我的新帖子',
        'body': '这是通过封装的客户端发送的内容。',
        'userId': 1
    }
    created_post = client.post("/posts", data=new_post_data)
    if created_post:
        print(f"创建成功！服务器返回的ID: {created_post['id']}")
        print(f"返回的标题: {created_post['title']}")

    # 4. 使用PUT请求更新一个帖子
    print("\n--- 3. 更新一个帖子 (PUT) ---")
    updated_data = {
        'id': 1,
        'title': '（已更新）标题',
        'body': '（已更新）内容',
        'userId': 1
    }
    updated_post = client.put("/posts/1", data=updated_data)
    if updated_post:
        print(f"更新成功！返回的标题: {updated_post['title']}")

    # 5. 使用DELETE请求删除一个帖子
    print("\n--- 4. 删除一个帖子 (DELETE) ---")
    # JSONPlaceholder的DELETE操作会返回一个空对象 {}
    is_deleted = client.delete("/posts/1")
    if is_deleted is not None:  # 删除成功时返回{}，布尔值为False，所以不能直接用if判断
        print("删除成功！")

    # 6. 测试一个不存在的接口，观察错误处理
    print("\n--- 5. 测试错误处理 ---")
    error_result = client.get("/non-existent-endpoint")
    if error_result is None:
        print("正如预期，请求失败并返回了None。")
